package com.liuyi.netty.im.server;

import com.liuyi.netty.im.commons.codes.ByteBuf2WebSocketEncoder;
import com.liuyi.netty.im.commons.codes.LimCodeDecoder;
import com.liuyi.netty.im.commons.codes.LimCodeEncode;
import com.liuyi.netty.im.commons.codes.WebSocket2ByteBufDecoder;
import com.liuyi.netty.im.server.handle.LimChannelHandler;
import com.liuyi.netty.im.server.handle.storage.ChannelStorage;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.timeout.IdleStateHandler;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

/**
 * @author Mr.Fmy
 * @since 2021-11-23
 */
@Slf4j
public class ImServer {

    private final EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    private final int tcpPort;
    private final int websocketPort;

//    public static void main(String[] args) throws Exception {
//        new LimController();
//        final ImServer imServer = new ImServer(new NioEventLoopGroup(), new NioEventLoopGroup(), 8080, 8081);
//        imServer.start();
//    }

    public ImServer(EventLoopGroup bossGroup, EventLoopGroup workerGroup, int tcpPort, int websocketPort) {
        this.bossGroup = bossGroup;
        this.workerGroup = workerGroup;
        this.tcpPort = tcpPort;
        this.websocketPort = websocketPort;
    }

    public void start() {
        try {
            final ChannelFuture tcpServer = this.tcpServer();
            final ChannelFuture websocketServer = this.websocketServer();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

    public void shutdown() {
        if (bossGroup != null) {
            bossGroup.shutdownGracefully();
        }
        if (workerGroup != null) {
            workerGroup.shutdownGracefully();
        }
        //关闭所有连接
        ChannelStorage.get().close().awaitUninterruptibly();
    }

    protected ChannelFuture websocketServer() throws Exception {
        ServerBootstrap b = new ServerBootstrap(); // (2)
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class) // (3)
                .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        //设置log监听器，并且日志级别为debug，方便观察运行流程
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS))
                                .addLast(new HttpServerCodec())
                                .addLast(new HttpObjectAggregator(65535))
                                .addLast(new HttpContentCompressor())
                                .addLast(new WebSocketServerProtocolHandler("/im", "IM", false, 65536))
                                .addLast(new WebSocket2ByteBufDecoder())
                                .addLast(new ByteBuf2WebSocketEncoder())
                                .addLast(new LimCodeDecoder())
                                .addLast(new LimCodeEncode())
                                .addLast(new LimChannelHandler());
                    }
                })
                .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

        // Bind and start to accept incoming connections.
        ChannelFuture f = b.bind(this.websocketPort).sync(); // (7)

        f.addListener(future -> {
            if (future.isSuccess()) {
                log.info("WebSocket Server启动成功，端口号：" + this.websocketPort);
            } else {
                log.info("WebSocket Server启动失败");
            }
        });
        return f;
    }

    protected ChannelFuture tcpServer() throws Exception {
        ServerBootstrap b = new ServerBootstrap(); // (2)
        b.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class) // (3)
                .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                    @Override
                    public void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new IdleStateHandler(60, 0, 0, TimeUnit.SECONDS));
                        //解码器
                        ch.pipeline().addLast(new LimCodeDecoder());
                        //编码器
                        ch.pipeline().addLast(new LimCodeEncode());
                        //自定义处理器
                        ch.pipeline().addLast(new LimChannelHandler());
                    }
                })
                .option(ChannelOption.SO_BACKLOG, 128)          // (5)
                .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

        // Bind and start to accept incoming connections.
        ChannelFuture f = b.bind(this.tcpPort).sync(); // (7)

        f.addListener(future -> {
            if (future.isSuccess()) {
                log.info("Tcp Server启动成功，端口号：" + this.tcpPort);
            } else {
                log.info("Tcp Server启动失败");
            }
        });

        return f;
    }
}
